#pragma once

#include "OMIT_MeshForwardDecl.h"

namespace OMIT_NAMESPACE
{
	template <bool PtrConst, typename Traits>
	class V_EdgeView 
		: public MeshViewBase<V_EdgeView<PtrConst, Traits>,
							  MeshTraits_PH<PtrConst, Traits>,
							  MeshTraits_PE<PtrConst, Traits>>
    {
		using PH = MeshTraits_PH<PtrConst, Traits>;
		using PE = MeshTraits_PE<PtrConst, Traits>;
		friend MeshViewBase<V_EdgeView<PtrConst, Traits>, PH, PE>;
		static PE Value(PH ph) { return ph->Edge(); }
		static PH Next(PH ph) { return ph->RotateNext(); }
	public:
		using MeshViewBase<V_EdgeView<PtrConst, Traits>, PH, PE>::MeshViewBase;
	};

	template <bool PtrConst, typename Traits>
	class V_VertView 
		: public MeshViewBase<V_VertView<PtrConst, Traits>,
							  MeshTraits_PH<PtrConst, Traits>,
							  MeshTraits_PV<PtrConst, Traits>>
    {
		using PH = MeshTraits_PH<PtrConst, Traits>;
		using PV = MeshTraits_PV<PtrConst, Traits>;
		friend MeshViewBase<V_VertView<PtrConst, Traits>, PH, PV>;
		static PV Value(PH ph) { return ph->End(); }
		static PH Next(PH ph) { return ph->RotateNext(); }
	public:
		using MeshViewBase<V_VertView<PtrConst, Traits>, PH, PV>::MeshViewBase;
	};

	template <typename Traits>
	bool TVert<Traits>::IsOnBoundary() const noexcept
    {
		if (IsIsolated())
			return true;

		for (auto* h : HalfEdge()->NextLoop()) {
			if (h->IsOnBoundary())
				return true;
		}

		return false;
	}

	template <typename Traits>
	H_RotaView<false, Traits> TVert<Traits>::OutHalfEdges()
    {
		if (IsIsolated())
	 		return {};

		return { HalfEdge(), HalfEdge() };
	}

	template <typename Traits>
	H_RotaView<true, Traits> TVert<Traits>::OutHalfEdges() const
    {
		const auto rst = const_cast<TVert*>(this)->OutHalfEdges();
		return *reinterpret_cast<const H_RotaView<true, Traits>*>(&rst);
	}

	template <typename Traits>
	V_EdgeView<false, Traits> TVert<Traits>::AdjEdges()
    {
		if (IsIsolated())
			return {};

		return { HalfEdge(), HalfEdge() };
	}

	template <typename Traits>
	V_EdgeView<true, Traits> TVert<Traits>::AdjEdges() const
    {
		const auto rst = const_cast<TVert*>(this)->AdjEdges();
		return *reinterpret_cast<const V_EdgeView<true, Traits>*>(&rst);
	}

	template <typename Traits>
	V_VertView<false, Traits> TVert<Traits>::AdjVertices()
    {
		if (IsIsolated())
			return {};

		return { HalfEdge(), HalfEdge() };
	}

	template <typename Traits>
	V_VertView<true, Traits> TVert<Traits>::AdjVertices() const
    {
		const auto rst = const_cast<TVert*>(this)->AdjVertices();
		return *reinterpret_cast<const V_VertView<true, Traits>*>(&rst);
	}

	template <typename Traits>
	std::size_t TVert<Traits>::Degree() const noexcept
    {
		std::size_t cnt = 0;
		for (auto* he : OutHalfEdges())
			++cnt;
		return cnt;
	}

	template<typename Traits>
	MeshTraits_H<Traits>* TVert<Traits>::FindFreeIncident() noexcept
    {
		if (IsIsolated())
			return nullptr;

		auto* begin = HalfEdge()->Pair();
		auto* h = begin;
		do {
			if (h->IsFree())
				return h;
			h = h->Next()->Pair();
		} while (h != begin);

		return nullptr;
	}

	template <typename Traits>
	MeshTraits_H<Traits>* TVert<Traits>::HalfEdgeTo(TV* end) noexcept
	{
		if (IsIsolated())
			return nullptr;

		auto* begin = HalfEdge();
		auto* h = begin;
		do {
			if (h->End() == end)
				return h;
			h = h->RotateNext();
		} while (h != begin);

		return nullptr;
	}

	template<typename Traits>
	MeshTraits_E<Traits>* TVert<Traits>::EdgeWith(TV* v) noexcept
    {
		auto* h = HalfEdgeTo(v);
		if (!h)
			return nullptr;

		return h->Edge();
	}
}
